import * as THREE from "three";

// copied from src/renderers/shaders/ShaderLib/meshphysical.glsl.js
export const vertex = /* glsl */`
#define STANDARD

varying vec3 vViewPosition;

#ifdef USE_TRANSMISSION

	varying vec3 vWorldPosition;

#endif

#include <common>
#include <uv_pars_vertex>
#include <displacementmap_pars_vertex>
#include <color_pars_vertex>
#include <fog_pars_vertex>
#include <normal_pars_vertex>
#include <morphtarget_pars_vertex>
#include <skinning_pars_vertex>
#include <shadowmap_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>

void main() {

	#include <uv_vertex>
	#include <color_vertex>
	#include <morphcolor_vertex>

	#include <beginnormal_vertex>
	#include <morphnormal_vertex>
	#include <skinbase_vertex>
	#include <skinnormal_vertex>
	#include <defaultnormal_vertex>
	#include <normal_vertex>

	#include <begin_vertex>
	#include <morphtarget_vertex>
	#include <skinning_vertex>
	#include <displacementmap_vertex>
	#include <project_vertex>
	#include <logdepthbuf_vertex>
	#include <clipping_planes_vertex>

	vViewPosition = - mvPosition.xyz;

	#include <worldpos_vertex>
	#include <shadowmap_vertex>
	#include <fog_vertex>

#ifdef USE_TRANSMISSION

	vWorldPosition = worldPosition.xyz;

#endif
}
`;

export const fragment = /* glsl */`
#define STANDARD

#ifdef PHYSICAL
	#define IOR
	#define USE_SPECULAR
#endif

uniform vec3 diffuse;
uniform vec3 emissive;
uniform float roughness;
uniform float metalness;
uniform float opacity;

#ifdef IOR
	uniform float ior;
#endif

#ifdef USE_SPECULAR
	uniform float specularIntensity;
	uniform vec3 specularColor;

	#ifdef USE_SPECULAR_COLORMAP
		uniform sampler2D specularColorMap;
	#endif

	#ifdef USE_SPECULAR_INTENSITYMAP
		uniform sampler2D specularIntensityMap;
	#endif
#endif

#ifdef USE_CLEARCOAT
	uniform float clearcoat;
	uniform float clearcoatRoughness;
#endif

#ifdef USE_IRIDESCENCE
	uniform float iridescence;
	uniform float iridescenceIOR;
	uniform float iridescenceThicknessMinimum;
	uniform float iridescenceThicknessMaximum;
#endif

#ifdef USE_SHEEN
	uniform vec3 sheenColor;
	uniform float sheenRoughness;

	#ifdef USE_SHEEN_COLORMAP
		uniform sampler2D sheenColorMap;
	#endif

	#ifdef USE_SHEEN_ROUGHNESSMAP
		uniform sampler2D sheenRoughnessMap;
	#endif
#endif

#ifdef USE_ANISOTROPY
	uniform vec2 anisotropyVector;

	#ifdef USE_ANISOTROPYMAP
		uniform sampler2D anisotropyMap;
	#endif
#endif

varying vec3 vViewPosition;

#include <common>
#include <packing>
#include <dithering_pars_fragment>
#include <color_pars_fragment>
#include <uv_pars_fragment>
#include <map_pars_fragment>
#include <alphamap_pars_fragment>
#include <alphatest_pars_fragment>
#include <alphahash_pars_fragment>
#include <aomap_pars_fragment>
#include <lightmap_pars_fragment>
#include <emissivemap_pars_fragment>
#include <iridescence_fragment>
#include <cube_uv_reflection_fragment>
#include <envmap_common_pars_fragment>
#include <envmap_physical_pars_fragment>
#include <fog_pars_fragment>
#include <lights_pars_begin>
#include <normal_pars_fragment>
#include <lights_physical_pars_fragment>
#include <transmission_pars_fragment>
#include <shadowmap_pars_fragment>
#include <bumpmap_pars_fragment>
#include <normalmap_pars_fragment>
#include <clearcoat_pars_fragment>
#include <iridescence_pars_fragment>
#include <roughnessmap_pars_fragment>
#include <metalnessmap_pars_fragment>
#include <logdepthbuf_pars_fragment>
#include <clipping_planes_pars_fragment>

void main() {

	#include <clipping_planes_fragment>

	vec4 diffuseColor = vec4( diffuse, opacity );
	ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );
	vec3 totalEmissiveRadiance = emissive;

	#include <logdepthbuf_fragment>
	#include <map_fragment>
	#include <color_fragment>
	#include <alphamap_fragment>
	#include <alphatest_fragment>
	#include <alphahash_fragment>
	#include <roughnessmap_fragment>
	#include <metalnessmap_fragment>
	#include <normal_fragment_begin>
	#include <normal_fragment_maps>
	#include <clearcoat_normal_fragment_begin>
	#include <clearcoat_normal_fragment_maps>
	#include <emissivemap_fragment>

	// accumulation
	#include <lights_physical_fragment>
	#include <lights_fragment_begin>
	#include <lights_fragment_maps>
	#include <lights_fragment_end>

	// modulation
	#include <aomap_fragment>

	vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;
	vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;

	#include <transmission_fragment>

	vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;

	#ifdef USE_SHEEN

		// Sheen energy compensation approximation calculation can be found at the end of
		// https://drive.google.com/file/d/1T0D1VSyR4AllqIJTQAraEIzjlb5h4FKH/view?usp=sharing
		float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor );

		outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular;

	#endif

	#ifdef USE_CLEARCOAT

		float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );

		vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc );

		outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat;

	#endif

	#include <opaque_fragment>
	#include <tonemapping_fragment>
	#include <colorspace_fragment>
	#include <fog_fragment>
	#include <premultiplied_alpha_fragment>
	#include <dithering_fragment>

}
`;

const keys = ['color', 'roughness', 'metalness', 'map', 'lightMap', 'lightMapIntensity', 'aoMap', 'aoMapIntensity',
  'emissive', 'emissiveIntensity', 'emissiveMap', 'bumpMap', 'bumpMapScale', 'normalMap', 'normalScale', 'displacementMap',
  'displacementScale', 'displacementBias', 'roughnessMap', 'metalnessMap', 'alphaMap', 'envMap', 'envMapIntensity',

  'clearcoatMap', 'clearcoatRoughness', 'clearcoatRoughnessMap', 'clearcoatNormalScale', 'clearcoatNormalMap', 'ior',
  'iridescenceMap', 'iridescenceIOR', 'iridescenceThicknessRange', 'iridescenceThicknessMap', 'sheenColor', 'sheenColorMap',
  'sheenRoughness', 'sheenRoughnessMap', 'transmissionMap', 'thickness', 'thicknessMap', 'attenuationDistance', 'attenuationColor',
  'specularIntensity', 'specularIntensityMap', 'specularColor', 'specularColorMap'];

const PbrDissolveShader = {
  shaderID: 'PbrDissolveShader',

  uniforms: {
    'iTime': { value: 1.0 },
    'modelHeight': { value: 1.0 }, // 模型高度，约定模型的原点位于底部投影的中心
    'modelWidth': { value: 1.0 },
    'modelDepth': { value: 1.0 },

    'mapNoise': { value: null },

  },

  vertexShader: vertex,

  fragmentShader: fragment,
}

const createPbrDissolveMtl = (height, width, depth, noiseTexture, holder) => {
  const materialPbrDissolve = new THREE.MeshStandardMaterial();
  materialPbrDissolve.onBeforeCompile = (shader) => {
    shader.uniforms.iTime = {value: 0.0};
    shader.uniforms.modelHeight = {value: height};
    shader.uniforms.modelWidth = {value: width};
    shader.uniforms.modelDepth = {value: depth};
    shader.uniforms.mapNoise = {value: noiseTexture};

    let token = '#define STANDARD';
    let insert = /* glsl */`
    varying float height;
    varying float width;
    varying float deep;
    uniform float modelHeight;
    uniform float modelWidth;
    uniform float modelDepth;
					`;
    shader.vertexShader = shader.vertexShader.replace( token, token + insert );

    token = 'void main() {'
    insert = /* glsl */`
  void main() {
    height = position.y + modelHeight * 0.5;
    width = position.x + modelWidth * 0.5;
    deep = position.z + modelDepth * 0.5;
					`;
    shader.vertexShader = shader.vertexShader.replace( token, insert );

    token = '#define STANDARD';
    insert = /* glsl */`
    precision highp float;
    precision highp sampler3D;
            
    #define epsilon .0001
    
    uniform sampler3D mapNoise;
    uniform float iTime;
    uniform float modelHeight;
    uniform float modelWidth;
    uniform float modelDepth;
    varying float height;
    varying float width;
    varying float deep;
    
    float sample1( vec3 p ) {
      return texture( mapNoise, p ).r;
    }
					`;
    shader.fragmentShader = shader.fragmentShader.replace( token, token + insert );
    token = 'void main() {';
    insert = /* glsl */`
    const float edgeWidth = 0.12;
    const float halfPI = 3.1415 * 0.5;
    void main() {
      vec3 uv = vec3(height / modelHeight, width / modelWidth, deep / modelDepth);
      float minVal = min(min(modelHeight, modelWidth), modelDepth);
      uv *= vec3(modelHeight, modelWidth, modelDepth) / minVal * 2.0;
      uv = fract(uv);
      float d = sample1( uv );
      float threshold = sin(iTime + halfPI) * 0.5 + 0.6;
      if (d > threshold) {
        discard;
      }
      if (d + edgeWidth > threshold) {
        float a = smoothstep(0.1, 0.9, (threshold - d) / edgeWidth);
        gl_FragColor = mix(vec4(0.1, 0.9, 0.95, 1.0), vec4(0.3, 0.3, 1.0, 1.0), a);
        return;
      }
					`;
    shader.fragmentShader = shader.fragmentShader.replace( token, insert );
    holder.push(shader);
  }
  return materialPbrDissolve;
}


export {createPbrDissolveMtl};
